
%{
#define YYSTYPE char*
/* Dla wersji Piotrka */
#include <grammar/util.h>
#include "model/AccessModifier.h"
#include "model/Attribute.h"
#include "model/Clazz.h"
#include "model/Method.h"
#include "model/Parameter.h"
#include "model/XMLSerializable.h"

/* Dla wersji Radka */

/*
#include "util.h"
#include "AccessModifier.h"
#include "Attribute.h"
#include "Clazz.h"
#include "Method.h"
#include "Parameter.h"
#include "XMLSerializable.h"


#include<iostream>
using namespace std;
*/



QMap<QString,Clazz*> tree;


QMap<QString,Clazz*> primitivesMap;
QMap<QString,Clazz*> unknownTypes;


extern "C"
{

        int yyparse(void);
        int yylex(void);  
        int yywrap()
        {
                return 1;
        }
		int yyerror(const char *);
}


// GLOBAL VARS ***************************************

QList<QString> defined_class;

// tymczasowe zmienne
QList<QString> base_classes;
QVector<Attribute*> fields;
QVector<Method*> methods;
QVector<Parameter*> fParameters;

AccessModifier current_specifier = PRIVATE;
QString current_classname;

bool functionNameIdentified = false;

%}

%token CLASS_KEY RIGHT_BRACE LEFT_BRACE SEMICOLON WHITESPACE WHATEVER COLON
%token COMMA ACCESS_SPECIFIER IDENTIFIER SIMPLE_TYPE_SPEC DECL_SPECIFIER
%token FUNC_SPECIFIER STORAGE_CLASS_SPECIFIER LEFT_CURLY_BRACE RIGHT_CURLY_BRACE
%token CV_QUALIFIER ASTERISK AMPERSAND


%%

commands: 
		| commands class_specifier 
;


class_specifier:
	class_head LEFT_BRACE member_specification RIGHT_BRACE SEMICOLON
	{
		tree[current_classname]->setAttributes(fields);
		fields.clear();
		tree[current_classname]->setMethods(methods);
		methods.clear();
		current_specifier = PRIVATE;
		current_classname = "";
	}
	
;

class_head:
		 CLASS_KEY identifier base_clause 
	{
		current_classname = $2;	
		defined_class.push_front($2);
		//qDebug() << "current_classname " << $2 << endl;
		/*current_classname = string($2);*/
		if(tree[$2] == 0) {
			tree[$2] = new Clazz($2);			
		}
		
		/* 
		 * Jade po klasach bazowych i dodaje dana klase jako dziecko
		 */
		QList<QString>::iterator it = base_classes.begin();
		for(; it != base_classes.end(); it++) {
			qDebug() << "Base class: " << *it << endl;
			// jesli klasa nadrzedna nie jest jeszcze zrobiona to ja tworze
			if(tree[*it] == 0) {
				tree[*it] = new Clazz(*it);
				//tree[*it]->children = new list<string>();
				//tree[*it]->parents = new list<string>();
			}
			QVector<Clazz*> children = tree[*it]->getSubtypes();
			children.push_front(tree[$2]);
			qDebug() << "Size of children of " << *it << " is " << children.size() << endl;
			tree[*it]->setSubtypes(children);
		}
		base_classes.clear();
		//display_recursive(string($2));
		//display_class(string($2), tree[$2]);
		
		free($2);
	}
;

/* opcjonalnie */
base_clause:
		   /* empty */
		   | COLON base_specifier_list
;

base_specifier_list:
			   inheritance_access_modifier base_specifier
				{ 
					/*printf("Base specifier: %s\n", $1);*/ 
					base_classes.push_front($2);
					
					free($2);
				}

				| base_specifier_list COMMA inheritance_access_modifier base_specifier
				{ 
					//printf("Base specifier list: %s\n", $3); 
					base_classes.push_front($4);
					free($4);
				}
;
inheritance_access_modifier :
						/* empty = private */
				| ACCESS_SPECIFIER 
				
base_specifier:
			  IDENTIFIER 
			  ;

/* opcjonalny identyfikator */
identifier:
			  /* empty */ 
			| IDENTIFIER  
;

		   
member_specification : /* empty*/ 
			| access_declaration member_declaration member_specification
			| member_declaration member_specification
;

access_declaration: 
				  ACCESS_SPECIFIER COLON 
				  {
					QString specifier = $1;
					current_specifier = specifier == "private" ? PRIVATE : ( specifier == "protected" ? PROTECTED : PUBLIC );
					//qDebug() << "current_specifier: " << current_specifier << endl;
				  }
;

member_declaration :
		   /*SIMPLE_TYPE_SPEC IDENTIFIER SEMICOLON 
			{ 
				instance_var member;
				member.varname = string($2);
				member.tname = string($1);
				//fields.push_front(member); 
				list<instance_var>* ptr = current_specifier == PUBLIC ?
										  &public_fields :
										  current_specifier == PRIVATE ?
										  &private_fields :
										  &protected_fields;
				ptr->push_front(member);
				free($1); free($2); 
			}*/
		   decl_specifier IDENTIFIER SEMICOLON 
			{
				/*instance_var member;
				member.varname = QString($2);
				member.tname = QString($1);
				//fields.push_front(member); 
				list<instance_var>* ptr = current_specifier == PUBLIC ?
										  &public_fields :
										  current_specifier == PRIVATE ?
										  &private_fields :
										  &protected_fields;
				ptr->push_front(member);*/
				Attribute* atr = new Attribute($2);
				// jesli jest typ prymitywny to ok
				if(primitivesMap[$1]) {
					atr->setClazz(*primitivesMap[$1]);
				}else {
					if(tree[$1] == 0) {
						tree[$1] = new Clazz($1);
						qDebug() << "Typu " << $1 << " nie ma w mapie wiec go dodaje\n";
					}
					atr->setClazz(*tree[$1]);
				}
				atr->setAccessModifier(current_specifier);
				fields.push_front(atr);
				free($1); free($2); 
			} 
		  |  decl_specifier declarator LEFT_CURLY_BRACE parameter_declaration_clause RIGHT_CURLY_BRACE SEMICOLON
			{
				Method* m = new Method($2);

				m->setAccessModifier(current_specifier);

				if(primitivesMap[$1]) {
					m->setReturnType(*primitivesMap[$1]);
				}else {
					if(tree[$1] == 0) {
						tree[$1] = new Clazz($1);
					}
					m->setReturnType(*tree[$1]);
				}
				m->setParameters(fParameters);
				methods.push_front(m);
				functionNameIdentified = false;
				fParameters.clear();
			}
; 


decl_specifier_seq :
				   /* empty */
					| decl_specifier_seq decl_specifier					 
;

decl_specifier :
	/* TODO dodac inne */
	function_specifier
	| storage_class_specifier
	/* TODO nominalnie tutaj jest type-specifier -> (simple-type-spec,
     * class-spec, enum-spec, etc). Zostawilem tylko simple lub identyfikator*/
	| SIMPLE_TYPE_SPEC { 
			if(functionNameIdentified) {
						Attribute* a = new Attribute($1);
						if(primitivesMap[$1]) {
							a->setClazz(*primitivesMap[$1]);
						}else {
							if(tree[$1] == 0) {
								tree[$1] = new Clazz($1);
							}
							a->setClazz(*tree[$1]);
						}
						fParameters.push_back(a);
					}
			}
	| IDENTIFIER {
				if(functionNameIdentified) {
						Attribute* a = new Attribute($1);
						if(primitivesMap[$1]) {
							a->setClazz(*primitivesMap[$1]);
						}else {
							if(tree[$1] == 0) {
								tree[$1] = new Clazz($1);
							}
							a->setClazz(*tree[$1]);
						}
						fParameters.push_back(a);
					}
				 }
	| DECL_SPECIFIER{ 
			if(functionNameIdentified) {
						Attribute* a = new Attribute($1);
						if(primitivesMap[$1]) {
							a->setClazz(*primitivesMap[$1]);
						}else {
							if(tree[$1] == 0) {
								tree[$1] = new Clazz($1);
							}
							a->setClazz(*tree[$1]);
						}
						fParameters.push_back(a);
					}
		}
;

storage_class_specifier :
	STORAGE_CLASS_SPECIFIER
;

function_specifier: 
	FUNC_SPECIFIER
;

declarator:
	/* @up uzywane przy deklaracji/definicji funkcji */
	direct_declarator
	| ptr_operator declarator
;


ptr_operator:
			ASTERISK cv_qualifier_seq
			| AMPERSAND 
;

cv_qualifier_seq:
				/* empty */
				| CV_QUALIFIER
					{
					}
;

direct_declarator:
				 declarator_id 
				{
					if(functionNameIdentified) {
						Parameter* p = fParameters.last();
						p->setName($1);
					}
					
					functionNameIdentified = true;
				}
				
;

declarator_id:
			 /* TODO tutaj powinien byc type-name */
			 IDENTIFIER
;

parameter_declaration_clause:
							/* empty */
					| parameter_declaration_list						
					{
					}
;

parameter_declaration_list:
						  parameter_declaration
						| parameter_declaration_list COMMA parameter_declaration
;

parameter_declaration:
					 decl_specifier_seq declarator
;

%%

#include "lex.yy.c"

extern int yydebug;
int yyerror(const char*s) {
	fprintf(stderr, "error: %s\n", s);
}

/* Jesli jest biblioteka iostream to mozna odkomentowac */
/*
void display_fields(Clazz* c, int depth) {
	QVector<Attribute*> f = c->getAttributes();
	//for(int i = 0; i < depth; qDebug() << " ", i++);
	//qDebug() << "Displaying attributes: " << endl;
	for(int i = 0; i < f.size(); i++) {
		QString access = f[i]->getAccessModifier() == PRIVATE ?
						"private" : f[i]->getAccessModifier() == PUBLIC ?
						"public"  : "protected";
		for(int j = 0; j < depth; cout << " ", j++);
		cout << access.toStdString() << ": " << f[i]->getClazz().getClassName().toStdString() << " " << f[i]->getName().toStdString() << endl;
	}
	QVector<Method*> m = c->getMethods();
	for(int i = 0; i < m.size(); i++) {
		QString access = m[i]->getAccessModifier() == PRIVATE ?
						"private" : m[i]->getAccessModifier() == PUBLIC ?
						"public"  : "protected";
		for(int j = 0; j < depth; cout << " ", j++);
		cout << access.toStdString() << " " << m[i]->getReturnType().getClassName().toStdString() << " " << m[i]->getName().toStdString() << endl;
		QVector<Parameter*> p = m[i]->getParameters();
		for(int k = 0; k < p.size(); k++) {
			cout << "param: " << p[k]->getName().toStdString()  << ", " << p[k]->getClazz().getClassName().toStdString() << " ";
		}
		cout << endl;
	}
}

void display_recursive(const QString & name, int depth = 0) {
	cout << "\n";
	for(int i = 0; i < depth; cout << " ", i++);


	// nazwa klasy
	cout << name.toStdString() << endl << endl;
	// pola
	display_fields(tree[name], depth);


	QVector<Clazz*> children = tree[name]->getSubtypes();
	for(int i = 0; i < children.size(); i++) {
		display_recursive(children[i]->getClassName(), depth+10);
	}
}
*/


void do_cleanup() {
	QMap<QString,Clazz*>::iterator it;
	QList<QString> toRemove;
	for(it = tree.begin(); it != tree.end(); it++) {
		//std::cout << it.key().toStdString() << ", value: " << it.value()->getClassName().toStdString() << std::endl;
		QVector<Attribute*> attr = it.value()->getAttributes();
		for(int i = 0; i < attr.size(); i++) {
			QString className = attr[i]->getClazz().getClassName();
			if(!defined_class.contains(className) && !primitivesMap[className]) {
				if(!unknownTypes[className]) {
					unknownTypes[className] = new Clazz(className);
				}
				toRemove.push_front(className);
				//delete tree[className];
				//tree[className] = 0;
				attr[i]->setClazz(*unknownTypes[className]);
				it.value()->setAttributes(attr);
				//std::cout << "\tnie ma zdefiniowanej klasy " << className.toStdString() << std::endl;
				//std::cout << attr[i]->getClazz().getClassName().toStdString() << std::endl;
			}
		}
	}
	for(QList<QString>::iterator itList = toRemove.begin(); itList != toRemove.end(); itList++) {
			delete tree[*itList];
			tree.remove(*itList);
			tree[*itList] = 0;
	}
}
void load_primitives_map() {
	primitivesMap["char"] = new Clazz("char");
	primitivesMap["wchar_t"] = new Clazz("wchar_t");
	primitivesMap["bool"] = new Clazz("bool");
	primitivesMap["short"] = new Clazz("short");
	primitivesMap["int"] = new Clazz("int");
	primitivesMap["long"] = new Clazz("long");
	primitivesMap["signed"] = new Clazz("signed");
	primitivesMap["unsigned"] = new Clazz("unsigned");
	primitivesMap["float"] = new Clazz("float");
	primitivesMap["double"] = new Clazz("double");
	primitivesMap["void"] = new Clazz("void");
}

/* dla celow zwiazanych z testowaniem */
/*
int main() {
	yyparse();
	display_recursive("A");
	return 0;
}
*/

QMap<QString,Clazz*>* parse(const char* filename, char *errorMsg) {
	load_primitives_map();

        yyin = fopen(filename, "rt");
        if(yyin == NULL){
            strcpy(errorMsg, "nie mozna otworzyc pliku");
            return NULL;
        }

        printf("parsowanie");
        if(yyparse()){
            strcpy(errorMsg, "blad parsowania!");
            return NULL;
        }

        fclose(yyin);
		do_cleanup();

        return &tree;
}




